/**
 * ajax, 使用浏览器原生fetch
 */
var ajax = function(opts) {
    var url = opts.url;

    var options = {
        method: (opts.method || 'get').toUpperCase(),
        headers: {
            "content-type": "application/x-www-form-urlencoded"
        }
    };
    if (options.method == 'POST') {
        if (opts.headers && opts.headers["content-type"]) {
            options.headers["content-type"] = opts.headers["content-type"]
        }

        // body
        if (options.headers["content-type"] == "application/json") {
            options.body = JSON.stringify(opts.data)
        } else if (typeof opts.data == 'string' || typeof opts.data == 'number') {
            options.body = opts.data;
        } else if (typeof opts.data == 'object') {
            options.body = queryEncode(opts.data);
        }
    }
    if (options.method == 'GET' && opts.params && (typeof opts.params == 'object')) {
        url = url + "?" + queryEncode(opts.params);
    }

    return fetch(url, options).then(response => {
        return response.json();
    }).then(res => {
        if (res.code != 0) {
            alert(res.msg);
            throw new Error(res.msg);
        }

        return res.data;
    })
}

function queryEncode(obj) {
    if (typeof obj != 'object') return "";

    var arr = [];
    for (var key in obj) {
        arr.push(key + "=" + encodeURIComponent(obj[key]));
    }

    return arr.join("&");
}


/*****************************************************************************
 * img base64
 */
function getImgBase64(url, cb) {
    if (!url || typeof cb != 'function') return;

    var xhr = new XMLHttpRequest();
    xhr.onload = function() {
        var fileReader = new FileReader();
        fileReader.onload = function(e) {
            cb(e.target.result);
        }

        fileReader.readAsDataURL(xhr.response);
    }

    xhr.open("get", url, true);
    xhr.responseType = "blob";
    xhr.send(null);
}


/*****************************************************************************
 * Edit Operations
 */
// When dblclick code, select its all content
document.addEventListener("dblclick", function(e) {
    if (e.target.tagName.toLowerCase() !== "code" 
        || !e.target.classList.contains("eye")) return;
    window.getSelection().selectAllChildren(e.target)
})

// Show Or Copy Password
document.addEventListener("click", function(e) {
    if (e.target.classList.contains("passwd-eye")) {
        var codeEl = e.target.previousElementSibling;
        if (codeEl && codeEl.classList.contains("app-passwd")) {
            if (codeEl.classList.contains("eye")) {
                codeEl.classList.remove("eye");
            } else {
                codeEl.classList.add("eye");
            }
        }
    }

    if (e.target.classList.contains("passwd-copy")) {
        var codeEl = e.target.previousElementSibling;
        if (codeEl) {
            codeEl = codeEl.previousElementSibling
        }
        if (codeEl && codeEl.classList.contains("app-passwd")) {
            copyEl(codeEl);
            // tip success
            e.target.classList.add("success");
            setTimeout(() => {
                e.target.classList.remove("success")
            }, 1200)
        }
    }

    if (e.target.classList.contains("outer-link")) {
        apis.openLink(e.target.getAttribute("data-href"))
    }
})

function deepcopy(obj) {
    if (!obj || typeof obj != "object" || typeof obj == "function") return obj;
    return JSON.parse(JSON.stringify(obj));
}

function copyEl(el) {
    var con = el.innerHTML.replaceAll("&lt;", "<").replaceAll("&gt;", ">");
    navigator.clipboard.writeText(con);
}


/*****************************************************************************
 * User Infos And ENV
 */
// Is file:: 
var isFielProtocol = location.host === "";

// 1: file, 2: server, 3: electron, 4: tauri
const ENV_FILE = 1;
const ENV_SERVER = 2;
const ENV_ELECTRON = 3;
const ENV_TAURI = 4;
const ENV = window.versions ? ENV_ELECTRON : (window.__TAURI__ ? ENV_TAURI : (isFielProtocol ? ENV_FILE : ENV_SERVER));

const passwdKey = "app_pkey";
const secrectKey = "app_secrectkey";
const prepageKey = "prepage";
const envStorage = (ENV == ENV_ELECTRON || ENV == ENV_TAURI) ? sessionStorage : localStorage;

const tauri = ENV == ENV_TAURI ? window.__TAURI__ : null;
const tauri_invoke = ENV  == ENV_TAURI ? window.__TAURI__.core.invoke : () => {};

function getPassword() {
    return envStorage.getItem(passwdKey)
}
function setPassword(password) {
    envStorage.setItem(passwdKey, password)
}
function clearPassword() {
    envStorage.removeItem(passwdKey)
}

function userLogin(password) {
    setPassword(password)
}
function userLogout() {
    clearPassword()
}

// username
function setUsername(username) {
    localStorage.setItem("app_user", username)
}
function getUsername() {
    return localStorage.getItem("app_user") || ""
}
function renderUsername() {
    var usernameEl = document.getElementById("app-username");
    if (usernameEl) {
        usernameEl.innerHTML = (getUsername() || "username")
    }
}
function initUsername() {
    if (isFielProtocol && document.body) {
        renderUsername()
    }
}
initUsername()



/*****************************************************************************
 * Apis
 */
// tauri apis
const tauriApis = {
    getAllCompany: () => tauri_invoke("get_company_all").then(res => JSON.parse(res)),
    getCompany: (id) => tauri_invoke("get_company", { payload: id }).then(res => JSON.parse(res)),
    addCompany: (entity) => tauri_invoke("add_company", { payload: JSON.stringify(entity) }).then(res => JSON.parse(res)),
    updateCompany: (entity) => tauri_invoke("update_company", { payload: JSON.stringify(entity) }).then(res => JSON.parse(res)),
    deleteCompany: (id) => tauri_invoke("delete_company", { payload: id }).then(res => JSON.parse(res)),

    getAllProject: () => tauri_invoke("get_project_all").then(res => JSON.parse(res)),
    getProject: (id) => tauri_invoke("get_project", { payload: id }).then(res => JSON.parse(res)),
    addProject: (entity) => tauri_invoke("add_project", { payload: JSON.stringify(entity) }).then(res => JSON.parse(res)),
    updateProject: (entity) => tauri_invoke("update_project", { payload: JSON.stringify(entity) }).then(res => JSON.parse(res)),
    deleteProject: (id) => tauri_invoke("delete_project", { payload: id }).then(res => JSON.parse(res)),

    getAllItem: () => tauri_invoke("get_item_all").then(res => JSON.parse(res)),
    getItem: (id) => tauri_invoke("get_item", { payload: id }).then(res => JSON.parse(res)),
    addItem: (entity) => tauri_invoke("add_item", { payload: JSON.stringify(entity) }).then(res => JSON.parse(res)),
    updateItem: (entity) => tauri_invoke("update_item", { payload: JSON.stringify(entity) }).then(res => JSON.parse(res)),
    deleteItem: (id) => tauri_invoke("delete_item", { payload: id }).then(res => JSON.parse(res)),

    getAllBookmark: () => tauri_invoke("get_bookmark_all").then(res => JSON.parse(res)),
    getBookmark: (id) => tauri_invoke("get_bookmark", { payload: id }).then(res => JSON.parse(res)),
    addBookmark: (entity) => tauri_invoke("add_bookmark", { payload: JSON.stringify(entity) }).then(res => JSON.parse(res)),
    updateBookmark: (entity) => tauri_invoke("update_bookmark", { payload: JSON.stringify(entity) }).then(res => JSON.parse(res)),
    deleteBookmark: (id) => tauri_invoke("delete_bookmark", { payload: id }).then(res => JSON.parse(res)),

    getUser: () => tauri_invoke("get_user").then(res => JSON.parse(res)),
    updateUser: (entity) => tauri_invoke("add_or_update_user", { payload: JSON.stringify(entity) }).then(res => JSON.parse(res)),

    getConfig: () => tauri_invoke("get_config").then(res => JSON.parse(res)),
    openLink: (url) => tauri_invoke("open_link", { url: url }),
    saveDb: () => Promise.reject("Not supported"),
}


// electron apis
const electronApis = {
    getAllCompany: () => companyApi.getAll(),
    getCompany: (id) => companyApi.get(id),
    addCompany: (entity) => companyApi.add(entity),
    updateCompany: (entity) => companyApi.update(entity),
    deleteCompany: (id) => companyApi.delete(id),

    getAllProject: () => projectApi.getAll(),
    getProject: (id) => projectApi.get(id),
    addProject: (entity) => projectApi.add(entity),
    updateProject: (entity) => projectApi.update(entity),
    deleteProject: (id) => projectApi.delete(id),

    getAllItem: () => itemApi.getAll(),
    getItem: (id) => itemApi.get(id),
    addItem: (entity) => itemApi.add(entity),
    updateItem: (entity) => itemApi.update(entity),
    deleteItem: (id) => itemApi.delete(id),

    getAllBookmark: () => bookmarkApi.getAll(),
    getBookmark: (id) => bookmarkApi.get(id),
    addBookmark: (entity) => bookmarkApi.add(entity),
    updateBookmark: (entity) => bookmarkApi.update(entity),
    deleteBookmark: (id) => bookmarkApi.delete(id),

    getUser: () => userApi.get(),
    updateUser: (entity) => userApi.update(entity),

    getConfig: () => configApi.get(),
    openLink: (url) => commonApi.openLink(rul),
    saveDb: () => Promise.reject("Not supported"),
}

// server apis
const serverApis = {
    getAllCompany: () => ajax({ url: "/company" }),
    getCompany: (id) => ajax({ url: "/company/" + id }),
    addCompany: (entity) => ajax({ url: "/company/add", method: "post", data: entity }),
    updateCompany: (entity) => ajax({ url: "/company/update", method: "post", data: entity }),
    deleteCompany: (id) => ajax({ url: "/company/delete", method: "post", data: {id: id} }),

    getAllProject: () => ajax({ url: "/project" }),
    getProject: (id) => ajax({ url: "/project/" + id }),
    addProject: (entity) => ajax({ url: "/project/add", method: "post", data: entity }),
    updateProject: (entity) => ajax({ url: "/project/update", method: "post", data: entity }),
    deleteProject: (id) => ajax({ url: "/project/delete", method: "post", data: {id: id} }),

    getAllItem: () => ajax({ url: "/item" }),
    getItem: (id) => ajax({ url: "/item/" + id }),
    addItem: (entity) => ajax({ url: "/item/add", method: "post", data: entity }),
    updateItem: (entity) => ajax({ url: "/item/update", method: "post", data: entity }),
    deleteItem: (id) => ajax({ url: "/item/delete", method: "post", data: {id: id} }),

    getAllBookmark: () => ajax({ url: "/bookmark" }),
    getBookmark: (id) => ajax({ url: "/bookmark/" + id }),
    addBookmark: (entity) => ajax({ url: "/bookmark/add", method: "post", data: entity }),
    updateBookmark: (entity) => ajax({ url: "/bookmark/update", method: "post", data: entity }),
    deleteBookmark: (id) => ajax({ url: "/bookmark/delete", method: "post", data: {id: id} }),

    getUser: () => ajax({ url: "/user" }),
    updateUser: (entity) => ajax({ url: "/user/update", method: "post", data: entity }),

    getConfig: () => ajax({ url: "/config" }),
    openLink: (url) => { window.open(url) },
    saveDb: () => ajax({ url: "/db", method: "post" }),
}

// file apis
const fileApis = {
    getAllCompany: () => {
        return Promise.resolve(_companys);
    },
    getCompany: () => {},
    addCompany: () => Promise.reject("Not support"),
    updateCompany: () => Promise.reject("Not support"),
    deleteCompany: () => Promise.reject("Not support"),

    getAllProject: () => {
        return Promise.resolve(_projects);
    },
    getProject: () => {},
    addProject: () => Promise.reject("Not support"),
    updateProject: () => Promise.reject("Not support"),
    deleteProject: () => Promise.reject("Not support"),

    getAllItem: () => {
        return Promise.resolve(_items);
    },
    getItem: () => {},
    addItem: () => Promise.reject("Not support"),
    updateItem: () => Promise.reject("Not support"),
    deleteItem: () => Promise.reject("Not support"),

    getAllBookmark: () => {
        return Promise.resolve(_bookmarkList);
    },
    getBookmark: () => {},
    addBookmark: () => Promise.reject("Not support"),
    updateBookmark: () => Promise.reject("Not support"),
    deleteBookmark: () => Promise.reject("Not support"),

    getUser: () => {
        return new Promise((resolve, reject) => {
            if (typeof _user != 'undefined' && _user != null) {
                resolve(_user)
            } else {
                reject()
            }
        })
    },
    updateUser: () => {
        return Promise.reject("Not support")
    },

    getConfig: () => {
        return new Promise((resolve, reject) => { 
            const defaultDbPath = window.location.href.split(/\/[a-z]+\.html$/)[0] + "/static/js2/"
            window.coreConfig = {
                serverPort: 5678,
                dbPath: defaultDbPath
            }
            resolve(window.coreConfig)
        })
    },
    openLink: (url) => { window.open(url) },
    saveDb: () => {
        return Promise.reject("Not support")
    },
}

const apis = ENV == ENV_ELECTRON ? electronApis : (ENV == ENV_TAURI ? tauriApis : (ENV == ENV_SERVER ? serverApis : fileApis))
console.log(ENV, apis)

/*****************************************************************************
 * Core Config
 */
var coreConfig = {}
const configPromise = apis.getConfig().then(config => {
    coreConfig = config || {};
    return coreConfig
})

// logout
window.onload = () => {
    window.document.body.addEventListener("click", e => {
        if (e.target.id == "to-login") {
            userLogout()
            location.replace("./login.html")
        }
    })

    configPromise.then(config => {
        var serverLink = document.getElementById("server-link")
        if (serverLink && config.serverPort) {
            serverLink.href = "http://localhost:" + config.serverPort
            serverLink.innerHTML = "http://localhost:" + config.serverPort
        }
    })
}

/*****************************************************************************
 * Toast
 */
function toast(content, type = "info") {
    const toastEl = document.getElementById("app-toast");
    if (!toastEl) return;

    if (type == "error") {
        toastEl.classList.add("error")
    } else {
        toastEl.classList.remove("error")
    }
    toastEl.querySelector("span").innerHTML = content;
    toastEl.classList.add("active");

    setTimeout(() => {
        toastEl.classList.remove("active");
    }, 1500);
}

function toastError(content) {
    toast(content, "error");
}


/*****************************************************************************
 * Electron
 */
if (window.commonApi) {
    window.commonApi.onUpdateRoute((route) => {
        if (route === "index") {
            window.location.replace("./index.html");
        } else if (route === "login") {
            window.location.replace("./login.html");
        } else if (route === "setting") {
            window.location.replace("./setting.html");
        } else if (route === "about") {
            window.location.replace("./about.html");
        } else if (route ==="help") {
            window.location.replace("./help.html");
        }
    });
}


/*****************************************************************************
 * secret key
 */
function getUserSecretKey() {
    return new Promise((resolve, reject) => {
        apis.getUser().then((user) => {
            var password = getPassword();
            if (!user || !user.secretKey || !password) {
                reject()
                return;
            } 

            getSecretKeyByPassword(user.secretKey, password).then(secretKey => { 
                resolve(secretKey)
            }).catch(e => {
                reject(e)
            })
        }).catch(e => {
            reject(e)
        })
    })

}

function getSecretKeyByPassword(secretKey, password) {
    return new Promise((resolve, reject) => {
        if (!secretKey || !password) {
            reject("Param Error");
            return
        }
       
        encryptor.decrypt(secretKey, password).then((originSecretKey) => {
            resolve(originSecretKey)
        }).catch(e => {
            reject(e)
        })
    })
}


